/**
 ***********************************************************************************************************
 * Interface allowing streaming of data
 ***********************************************************************************************************
 * @file RBStream.h
 ***********************************************************************************************************/

#ifndef __RBSTREAM_H__
#define __RBSTREAM_H__

#ifdef  HEADER_ONCE
#pragma HEADER_ONCE
#endif

#ifdef  HEADER_MSG
#pragma HEADER_MSG(RBStream.h)
#endif

/***********************************************************************************************************
 * Includes
 ***********************************************************************************************************/

/***********************************************************************************************************
 * Forward declarations
 ***********************************************************************************************************/
#include "RBCommon.h"
#include "RBString.h"
#include "RBStream.h"
#include "RBHandle.h"

/***********************************************************************************************************
 * Classes
 ***********************************************************************************************************/
class RBObject;
class RBSceneObject;
class RBStreamReader;
RBDeclareHandle(RBEntity);

#define RBRegisterStreamClass(ClassName) RBStream::RegisterRTTI(#ClassName, RBStream::CreateFunction(&ClassName::CreateObject))

class RBStream
{
public:
    enum SerializeMode
    {
        SM_READ = 0,
        SM_WRITE
    };

                    RBStream        ();
                   ~RBStream        ();

    void            Serialize       (SerializeMode eMode);
    void            Insert          (RBEntityHandle entity);
    RBEntityHandle  GetObjectAt     (uint32);
    uint32          GetObjectCount  () const;

    // Unified way of getting files: use directly serialize when you know the file is in rbs format: will be faster
    static RBStream* Load           (const RBString& strFilename);


    // In order to have custom formats: create your own reader and register it
    static void     RegisterReader  (RBStreamReader* pReader);
    static void     UnregisterReader(RBStreamReader* pReader);
    static void     ClearReaders    ();


    // Begin Internal Usage
    typedef fastdelegate::FastDelegate<RBObject*(RBStream&)> CreateFunction;
    static void     RegisterRTTI    (const RBString& strRTTI, CreateFunction createFunc);
    static void     UnregisterRTTI  (const RBString& strRTTI, CreateFunction createFunc);

    bool            IsWriting       () const { return m_bWrite; }
    bool            IsReading       () const { return !IsWriting(); }


    // Needed to be able to save links between objects: all objects must 1st be registered before Saving/Loading or pointers won't work
    template <typename T>
    bool            Register        (T* pObject);
    template <typename T>
    bool            Register        (RBSmartPointer<T> spObject);


    // Save/Load operations: during saving, Pointers are guaranteed to remain unchanged
    template <typename T>
    void            Link            (T*& pObject);
    template <typename T>
    void            Link            (RBSmartPointer<T>& spObject);


    // Value writing: during save, parameter is guaranteed to remain unchanged
    template <typename T>
    RBStream&       operator&       (T& t);


    template <typename T>
    void            SaveLink        (T* pObject);
    template <typename T>
    void            SaveLink        (RBSmartPointer<T> spObject);


    template <typename T>
    void            LoadLink        (T*& pObject);
    template <typename T>
    void            LoadLink        (RBSmartPointer<T>& spObject);

    template <typename T>
    RBStream&       operator<<      (const T& t);
    template <typename T>
    void            Write           (const T& t);
    template <typename T>
    void            Write           (const std::vector<T*>& v);
    template <typename T>
    void            Write           (const std::vector<RBSmartPointer<T> >& v);
    template <typename Type, uint32 Count, template<typename T, uint32 C> class RBVectorBaseType>
    void            Write           (const RBVector<Type, Count, RBVectorBaseType>& v);
    template <typename Type, uint32 Rows, uint32 Cols>
    void            Write           (const RBMatrix<Type, Rows, Cols>& m);

    template <typename T>
    void            Write           (const T* pT, uint32 uiCount);
    template <>
    void            Write           (const RBString* pStr, uint32 uiCount);

    template <typename T>
    RBStream&       operator>>      (T& t); 
    template <typename T>
    void            Read            (T& t);
    template <typename T>
    void            Read            (std::vector<T*>& v);
    template <typename T>
    void            Read            (std::vector<RBSmartPointer<T> >& v);


    template <typename T>
    void            Read            (T* pT, uint32 uiCount);
    template <>
    void            Read            (RBString* pStr, uint32 uiCount);

    // By default, does nothing: allows custom readers to instantiate base class since they won't do anything
    virtual void    WriteBuffer     (const void*, uint32) {}
    virtual void    ReadBuffer      (void*, uint32) {}

    RBObject*       CreateObject    ();
    void            WriteRTTI       (const RBRTTI* pRTTI, RBObject* pObject);
    RBObject*       CreateObject    (const RBString& strRTTI);

    // End Internal Usage

protected:
    void            RegisterPointer     (void* pObject, uint32 uiID);

private:
    // We want to prevent assignements
    RBStream&       operator=       (const RBStream&);


protected:
    static const uint32 ms_INVALID_ID = UINT_MAX;
    static const uint32 ms_NULL_ID = 0;
    typedef std::map<void*, uint32> ObjectMap;
    typedef std::map<uint32, void*> IDMap;
    typedef std::vector<RBObject*> ObjectVector;

    ObjectMap       m_mRegisteredObjects;
    IDMap           m_mRegisteredIDs;
    uint32          m_uiID;
    ObjectVector    m_vObjectsRegistered;
    std::vector<RBEntityHandle> m_vTopObjects;

    typedef std::map<RBString, CreateFunction> RBObjectMap; 
    static RBObjectMap ms_mCreators;
    static std::set<RBStreamReader*>    ms_sReaders;

    bool                m_bWrite;
};

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template <typename T>
bool RBStream::Register(T* pObject)
{
    ObjectMap::iterator iter = m_mRegisteredObjects.find(pObject);
    if (iter == m_mRegisteredObjects.end())
    {
        RegisterPointer(pObject, m_uiID++);
        return true;
    }
    return false;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
bool RBStream::Register(RBSmartPointer<T> spObject)
{
    return Register(RBSmartPointerCast(T, spObject));
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
inline void RBStream::Link( T*& pObject )
{
    if (m_bWrite)
    {
        SaveLink(pObject);
    }
    else
    {
        LoadLink(pObject);
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
inline RBStream& RBStream::operator&( T& t )
{
    if (m_bWrite)
    {
        Write(t);
    }
    else
    {
        Read(t);
    }
    return *this;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Link( RBSmartPointer<T>& spObject )
{
    if (m_bWrite)
    {
        SaveLink(spObject);
    }
    else
    {
        LoadLink(spObject);
    }
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template <typename T>
void RBStream::SaveLink(T* pObject)
{
    if (pObject != NULL)
    {
        ObjectMap::iterator iter = m_mRegisteredObjects.find(pObject);
        if (iter == m_mRegisteredObjects.end())
        {
            RBAssert(false, "Warning: trying to save a link to an object which has not been registered");
            Write(ms_NULL_ID);
        }
        else
        {
            Write(iter->second);
        }
    }
    else
    {
        Write(ms_NULL_ID);
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::SaveLink(RBSmartPointer<T> spObject)
{
    SaveLink(RBSmartPointerCast(T, spObject));
}


/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template <typename T>
void RBStream::LoadLink(T*& pObject)
{
    uint32 uiID;
    Read(uiID);
    if (uiID == ms_NULL_ID)
    {
        pObject = NULL;
    }
    else if (uiID == ms_INVALID_ID)
    {
        RBAssert(false, "Invalid Link ID");
        pObject = NULL;
    }
    else
    {
        pObject = static_cast<T*>(m_mRegisteredIDs[uiID]);
        RBAssert(pObject != NULL, "Valink Link ID but object is not registered !");
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::LoadLink(RBSmartPointer<T>& spObject)
{
    T* pObject;
    LoadLink(pObject);
    spObject = pObject;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
inline RBStream::RBStream()
: m_uiID(1)
{

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
inline RBStream::~RBStream()
{

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template <typename T>
RBStream& RBStream::operator<<(const T& t)
{
    Write<T>(t);
    return *this;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Write(const T& t)
{
    Write<T>(&t, 1);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Write( const std::vector<T*>& v )
{
    uint32 uiCount = v.size();
    Write(uiCount);
    for (uint32 ui=0; ui < v.size(); ++ui)
    {
        Link(v[ui]);
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Write( const std::vector<RBSmartPointer<T> >& v )
{
    uint32 uiCount = v.size();
    Write(uiCount);
    for (uint32 ui=0; ui < v.size(); ++ui)
    {
        SaveLink(v[ui]);
    }
}


/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename Type, uint32 Count, template<typename T, uint32 C> class RBVectorBaseType>
void RBStream::Write( const RBVector<Type, Count, RBVectorBaseType>& v )
{
    for (uint32 ui=0; ui < Count; ++ui)
    {
        Write<Type>(v(ui));
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename Type, uint32 Rows, uint32 Cols>
void RBStream::Write( const RBMatrix<Type, Rows, Cols>& m )
{
    for (uint8 uiRow=0; uiRow<Rows; ++uiRow)
        for (uint8 uiCol=0; uiCol<Cols; ++uiCol)
        {
            Write<Type>(m(uiRow,uiCol));
        }

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Write(const T* pT, uint32 uiCount)
{
    WriteBuffer(pT, uiCount * sizeof(T));
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template <typename T>
RBStream& RBStream::operator>>(T& t)
{
    Read<T>(t);
    return *this;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Read(T& t)
{
    Read<T>(&t, 1);
}


/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Read( std::vector<T*>& v )
{
    uint32 uiCount;
    Read(uiCount);
    for (uint32 ui=0; ui < uiCount; ++ui)
    {
        T* p;
        LoadLink(p);
        v.push_back(p);
    }
}
/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Read( std::vector<RBSmartPointer<T> >& v )
{
    uint32 uiCount;
    Read(uiCount);
    for (uint32 ui=0; ui < uiCount; ++ui)
    {
        T* p;
        Link(p);
        v.push_back(p);
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template <typename T>
void RBStream::Read(T* pT, uint32 uiCount)
{
    ReadBuffer(pT, uiCount * sizeof(T));
}

/***********************************************************************************************************/

#endif // __RBSTREAM_H__
